package cbfg.rvadapter

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import cbfg.rvadapter.RVAdapter.Companion.RV_FLAG_CLEAR_SELECTIONS
import cbfg.rvadapter.RVAdapter.Companion.RV_FLAG_NOTIFY_CHANGED
import cbfg.rvadapter.selection.ISelectionDelegate
import cbfg.rvadapter.selection.NoSelectionDelegate

/**
 * @author:  Tom Hawk
 * 添加时间: 2020/8/26 13:09
 * 功能描述:
 */
class RVAdapter<T : Any>(
    context: Context,
    private val rvHolderFactory: RVHolderFactory,
    private val selectionDelegate: ISelectionDelegate<T> = NoSelectionDelegate()
) : RecyclerView.Adapter<RVHolder<Any>>(), ISelectionDelegate<T> by selectionDelegate {
    private val inflater = LayoutInflater.from(context)

    private val items = ArrayList<T>()
    private var itemClickListener: ItemClickListener<T>? = null
    private var itemLongClickListener: ItemClickListener<T>? = null

    /** 记录 item 数据类型对应的 viewType */
    private val viewTypeMap = HashMap<Class<*>, Int>()

    /**
     * 如果需要处理 adapter 中的一些事件则传入此对象实例
     */
    private var lifecycleHandler: RVLifecycleHandler? = null

    init {
        selectionDelegate.setup(this, items)
    }

    fun bind(rv: RecyclerView): RVAdapter<T> {
        rv.adapter = this
        return this
    }

    fun setItemClickListener(listener: ItemClickListener<T>): RVAdapter<T> {
        this.itemClickListener = listener
        return this
    }

    fun setItemLongClickListener(listener: ItemClickListener<T>): RVAdapter<T> {
        this.itemLongClickListener = listener
        return this
    }

    fun setLifecycleHandler(lifecycleHandler: RVLifecycleHandler): RVAdapter<T> {
        this.lifecycleHandler = lifecycleHandler
        return this
    }

    fun getItems(): List<T> = items

    fun getItem(position: Int): T? = items.getOrNull(position)

    fun getSelection(index: Int): T? = getSelections().elementAtOrNull(index)

    /**
     * 设置数据集
     * @param flags 可以是以下组合：
     * [RV_FLAG_CLEAR_SELECTIONS] 清空该数据类型的选中项
     * [RV_FLAG_NOTIFY_CHANGED] 刷新
     */
    fun setItems(items: List<T>?, flags: Int = RV_FLAG_DEFAULT): RVAdapter<T> {
        clear(flags and RV_FLAG_CLEAR_SELECTIONS)
        if (!items.isNullOrEmpty()) {
            this.items.addAll(items)
        }
        if (flags.shouldNotifyChanged) {
            notifyDataSetChanged()
        }
        return this
    }

    fun add(index: Int = items.size, item: T) {
        items.add(index, item)
        notifyItemInserted(index)
    }

    fun addList(index: Int = items.size, list: Collection<T>) {
        items.addAll(index, list)
        notifyItemRangeInserted(index, list.size)
    }

    /**
     * 使用这个方法需要注意 item 的唯一确定性，否则可能导致删除出错。
     * 比如 items: [k, t, o,  , o, a]，想删除 index=4 的 'o' 会变成删除了 index=2 的 'o'。
     * 避免删除出错，有两个方法：
     * 1、确保 item 的唯一确定性（覆写 item 的 hashCode 和 equals 方法，如果无法实现唯一确定性则只能使用方法二）；
     * 2、使用 [removeAt] 方法；
     */
    fun remove(item: T, shouldNotify: Boolean = true): Boolean {
        return removeAt(item, items.indexOf(item), shouldNotify)
    }

    fun removeAt(index: Int, shouldNotify: Boolean = true): Boolean {
        return removeAt(items.getOrNull(index), index, shouldNotify)
    }

    private fun removeAt(item: T?, index: Int, shouldNotify: Boolean): Boolean {
        item ?: return false
        if (index !in 0 until items.size) return false

        deselect(item, false)
        //这个方法可能导致删错 item，比如 items: [k, t, o,  , o, a]，想删除 index=4 的 'o' 会变成删除了 index=2 的 'o'：
        //items.remove(item)
        items.removeAt(index)
        if (shouldNotify) {
            notifyItemRemoved(index)
        }
        return true
    }

    fun removeList(list: Collection<T>) {
        if (list.isNotEmpty()) {
            deselectAll(list, null, false)
            items.removeAll(list.toSet())
            notifyDataSetChanged()
        }
    }

    fun removeRange(fromIndex: Int, toIndex: Int) {
        val list = items.subList(fromIndex, toIndex)
        if (list.isNotEmpty()) {
            deselectAll(list, IntRange(fromIndex, toIndex), false)
            list.clear()
            notifyItemRangeRemoved(fromIndex, toIndex - fromIndex)
        }
    }

    /**
     * 替换指定位置的 item
     */
    fun replace(newItem: T, position: Int) {
        removeAt(items.getOrNull(position), position, false)
        items.add(position, newItem)
        notifyItemChanged(position)
    }

    /**
     * 清除数据
     * @param flags 可以是以下组合：
     * [RV_FLAG_CLEAR_SELECTIONS] 清空该数据类型的选中项
     * [RV_FLAG_NOTIFY_CHANGED] 刷新
     */
    fun clear(flags: Int = RV_FLAG_DEFAULT) {
        items.clear()
        if (flags.shouldClearSelections) {
            //这里不用通知刷新，由 flags 去控制：
            deselectAll(false)
        }
        if (flags.shouldNotifyChanged) {
            notifyDataSetChanged()
        }
    }

    /**
     * 根据 item 类获取对应的 viewType
     */
    private fun getItemViewType(clazz: Class<*>): Int {
        val viewType = viewTypeMap[clazz]
        return if (viewType != null) viewType else {
            val newViewType = viewTypeMap.size
            viewTypeMap[clazz] = newViewType
            return newViewType
        }
    }

    override fun getItemViewType(position: Int): Int {
        val viewType = rvHolderFactory.getItemViewType(position, items[position])
        return if (viewType != -1) viewType else getItemViewType(items[position].javaClass)
    }

    override fun getItemCount(): Int = items.size

    @Suppress("UNCHECKED_CAST")
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RVHolder<Any> {
        val itemClazz = viewTypeMap.entries.find { it.value == viewType }?.key
        val holder = rvHolderFactory.createViewHolder(inflater, parent, viewType, itemClazz)
        holder.setListeners(
            itemClickListener?.run { View.OnClickListener { onItemClick(holder, it, this) } },
            itemLongClickListener?.run {
                View.OnLongClickListener {
                    onItemClick(holder, it, this)
                    true
                }
            })
        return holder as RVHolder<Any>
    }

    private fun onItemClick(holder: RVHolder<out Any>, view: View, clicker: ItemClickListener<T>?) {
        val position = holder.bindingAdapterPosition
        if (position in 0 until items.size) {
            clicker?.onCall(view, items[position], position)
        }
    }

    override fun onBindViewHolder(holder: RVHolder<Any>, position: Int) {
        val item = items[position]
        holder.setContent(item, null)
        holder.onSelectedState(item, isSelected(item))
    }

    override fun onBindViewHolder(holder: RVHolder<Any>, position: Int, payloads: List<Any>) {
        // 如果 payloads 为空，调用全量绑定方法:
        if (payloads.isEmpty()) {
            onBindViewHolder(holder, position)
            return
        }
        //判断是否选中状态变化，是则调用对应回调方法：
        val item = items[position]
        when (val payload = payloads[0]) {
            RV_PAYLOAD_SELECTED -> holder.onSelectedState(item, true)
            RV_PAYLOAD_DESELECTED -> holder.onSelectedState(item, false)
            else -> holder.setContent(item, payload)
        }
    }

    override fun onViewRecycled(holder: RVHolder<Any>) {
        super.onViewRecycled(holder)
        lifecycleHandler?.onViewRecycled(holder)
    }

    override fun onFailedToRecycleView(holder: RVHolder<Any>): Boolean {
        lifecycleHandler?.run { return this.onFailedToRecycleView(holder) }
        return super.onFailedToRecycleView(holder)
    }

    override fun onViewAttachedToWindow(holder: RVHolder<Any>) {
        super.onViewAttachedToWindow(holder)
        lifecycleHandler?.onViewAttachedToWindow(holder)
    }

    override fun onViewDetachedFromWindow(holder: RVHolder<Any>) {
        super.onViewDetachedFromWindow(holder)
        lifecycleHandler?.onViewDetachedFromWindow(holder)
    }

    /** item 点击、长按事件 */
    fun interface ItemClickListener<T> {
        fun onCall(view: View, item: T, position: Int)
    }

    companion object {
        /** 选中 */
        const val RV_PAYLOAD_SELECTED = 10101

        /** 未选中 */
        const val RV_PAYLOAD_DESELECTED = 10102

        /** 可选 */
        const val RV_PAYLOAD_SELECTABLE = 10103

        /** 不可选 */
        const val RV_PAYLOAD_UNSELECTABLE = 10104

        /** item 更新 */
        const val RV_PAYLOAD_ITEM_CHANGED = 10105

        /** 是否清除对应选中项 */
        const val RV_FLAG_CLEAR_SELECTIONS = 1

        /** 是否通知数据更新 */
        const val RV_FLAG_NOTIFY_CHANGED = 2

        /** 默认清除及通知更新 */
        const val RV_FLAG_DEFAULT = RV_FLAG_CLEAR_SELECTIONS or RV_FLAG_NOTIFY_CHANGED
    }
}

private inline val Int.shouldClearSelections: Boolean
    get() = (this and RV_FLAG_CLEAR_SELECTIONS) != 0

private inline val Int.shouldNotifyChanged: Boolean
    get() = (this and RV_FLAG_NOTIFY_CHANGED) != 0